package com.kedacom.ctsp.authz.oauth2.client.provider;

import com.kedacom.ctsp.authz.entity.*;
import com.kedacom.ctsp.authz.oauth2.client.service.OAuth2Service;
import com.kedacom.ctsp.authz.oauth2.core.OAuth2Authentication;
import com.kedacom.ctsp.authz.oauth2.core.vo.AccessToken;
import com.kedacom.ctsp.authz.security.provider.AuthUserDetails;
import com.kedacom.ctsp.lang.mapper.BeanMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 在跳转登录之前拦截,如果已经sso单点登录了.则直接根据accessToken clientId clientSecret 获取用户信息
 *
 * @author majun
 * @create 2018/5/24
 **/
@Slf4j
public class RequestLoginUrlFilter extends OncePerRequestFilter {

    @Autowired
    private OAuth2Service oauthService;

    /**
     * 通过参数中是否存在accessToken, 并且accessToken有效 被过滤
     *
     * @param request
     * @return
     */
    private boolean isInclude(HttpServletRequest request) {
        String accessToken = request.getParameter("accessToken");
        log.info("accessToken: {} ", accessToken);
        if (StringUtils.isBlank(accessToken)) {
            return true;
        }
        return false;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {

        String accessTokenStr = request.getParameter("accessToken");
        AccessToken accessToken = AccessToken.builder().accessToken(accessTokenStr).build();
        try {
            OAuth2Authentication oAuth2Authentication = oauthService.getAuthenticationByAccessToken(accessToken);
            if (oAuth2Authentication == null) {
                filterChain.doFilter(request, response);
                return;
            }
            oAuth2Authentication.setAccessToken(AccessToken.builder().accessToken(accessTokenStr).build());
            SimpleAuthentication authentication = new SimpleAuthentication();
            BeanMapper.deepCopy(oAuth2Authentication, authentication);

            Set<AuthRole> authRoles = oAuth2Authentication.getRoles().stream().map(r -> {
                AuthRole authRole = new AuthRole();
                BeanMapper.deepCopy(r, authRole);
                return authRole;
            }).collect(Collectors.toSet());
            authentication.setRoles(authRoles);
            authentication.getRoleSigns();
            //TODO
//            Set<AuthResource> authResources = oAuth2Authentication.getResources().stream().map(r -> {
//                AuthResource authResource = new AuthResource();
//                BeanMapper.deepCopy(r, authResource);
//                return authResource;
//            }).collect(Collectors.toSet());
//
//            authentication.setResources(authResources);
//            authentication.getResourceSigns();

            AuthUserDetails principal = new AuthUserDetails(authentication);
            PreAuthenticatedAuthenticationToken auth =
                    new PreAuthenticatedAuthenticationToken(principal, accessToken, principal.getAuthorities());
            SecurityContextHolder.getContext().setAuthentication(auth);

            HttpSession session = request.getSession(true);
            session.setAttribute("SPRING_SECURITY_CONTEXT", SecurityContextHolder.getContext());
            return;

        } catch (Exception e) {
            logger.error(e);
        }

        filterChain.doFilter(request, response);
    }

    @Override
    protected boolean shouldNotFilter(HttpServletRequest request) {
        // 暂时先不用,client不用切换
        return true;
//        return isInclude(request);
    }
}
